/*
 * visual.c
 *
 *  Created on: Nov 28, 2010
 *      Author: cumeo89
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include "visual.h"
#include "util.h"

typedef struct {
	Node *node;
	int rank;
} NodeRank;

NodeRank *nr_new(Node *node, int rank) {
	NodeRank *nr = g_new(NodeRank, 1);
	nr->node = node;
	nr->rank = rank;
	return nr;
}

void writeNodes(FILE *f, SuffixTree *tree, int edgeLabelType) {
	gboolean leafLabel = ((edgeLabelType != EDGE_LABEL_TYPE_INDEX) && (pointerArrSize(tree->strings) > 1));
	GQueue *queue = g_queue_new();
	g_queue_push_head(queue, nr_new(tree->root, 0));
	int currentRank = -1;
	while (!g_queue_is_empty(queue)) {
		NodeRank *nr = g_queue_pop_head(queue);
		if (nr->rank != currentRank) {
			if (currentRank >= 0) {
				fprintf(f, " }\n");
			}
			currentRank = nr->rank;
			fprintf(f, "{ rank=\"same\"; ");
		}

		fprintf(f, "\"%p\" [", nr->node);
		if (n_isLeaf(nr->node)) {
			fprintf(f, "shape=box");
			if (leafLabel && nr->node->parentEdge != NULL) {
				fprintf(f, ", label=\"%d\"", nr->node->parentEdge->stringIndex);
			}
		}
		fprintf(f, "]; ");

		g_slist_foreach(iter, nr->node->edges) {
			Node *child = ((Edge *)iter->data)->child;
			g_queue_push_tail(queue, nr_new(child, nr->rank+1));
		}

		g_free(nr);
	}
	fprintf(f, " }\n");
	g_queue_free(queue);
}

void writeEdgesAndLinks(FILE *f, SuffixTree *tree, int type, glong i, glong j) {
	gboolean stringIndexIncluded = (pointerArrSize(tree->strings) > 1);
	GQueue *queue = g_queue_new();
	g_queue_push_head(queue, tree->root);
	while (!g_queue_is_empty(queue)) {
		Node *node = g_queue_pop_head(queue);
		if (node->link != NULL) {
			fprintf(f, "\"%p\" -> \"%p\"  [style=\"dashed\", color=red];\n", node, node->link);
		}
		g_slist_foreach(iter, node->edges) {
			Edge *edge = iter->data;
			Node *child = edge->child;
			glong end = edge->end < 0 ? i : edge->end;
			if (type == EDGE_LABEL_TYPE_SUBSTRING) {
				gchar *substr = utf8_substring(tree->strings[edge->stringIndex], edge->start, end);
				fprintf(f, "\"%p\" -> \"%p\"  [label=\"%s\", dir=\"none\"];\n",
										node, child, substr);
				free(substr);
			} else {
				if (stringIndexIncluded) {
					fprintf(f, "\"%p\" -> \"%p\"  [label=\"%d(%ld,%ld)\", dir=\"none\"];\n",
							node, child, edge->stringIndex, edge->start, end);
				} else {
					fprintf(f, "\"%p\" -> \"%p\"  [label=\"(%ld,%ld)\", dir=\"none\"];\n",
							node, child, edge->start, end);
				}
			}
			g_queue_push_tail(queue, child);
		}
	}
	g_queue_free(queue);
}

void writeTree(FILE *f, SuffixTree *tree, int edgeLabelType, glong i, glong j) {
	fprintf(f, "digraph {\n");
	fprintf(f, "node [shape=circle, label=\"\", width=0.1, height=0.1];\n");
	fprintf(f, "edge [arrowsize=0.5];\n");
	writeNodes(f, tree, edgeLabelType);
	writeEdgesAndLinks(f, tree, edgeLabelType, i, j);
	fprintf(f, "}");
}

void generateDotFiles(SuffixTree *tree, VisualizerData *data, glong i, glong j) {
	gchar path[1024];
	sprintf(path, "%s%c%d.dot", data->dir, G_DIR_SEPARATOR, data->counter);
	FILE *f = fopen(path, "wt");
	writeTree(f, tree, data->edgeLabelType, i, j);
	fclose(f);
}

void get_image_path(gchar *s, VisualizerData *data, int index) {
	sprintf(s, "%s%c%03d.png", data->dir, G_DIR_SEPARATOR, index+1);
}

void generateImage(SuffixTree *tree, VisualizerData *data, glong i, glong j) {
	gchar dotName[PATH_MAX];
	gchar dotPath[PATH_MAX];
	snprintf(dotName, PATH_MAX, "%03d.dot", data->counter);
	snprintf(dotPath, PATH_MAX, "%s%c%s", data->dir, G_DIR_SEPARATOR, dotName);
	FILE *f = fopen(dotPath, "wt");
	writeTree(f, tree, data->edgeLabelType, i, j);
	fclose(f);

	gchar pngName[PATH_MAX];
	snprintf(pngName, PATH_MAX, "-o%03d.png", data->counter);
	gchar *argv[] = { data->dot, pngName, "-Tpng", dotName, NULL };
	gint status;
	GError *error;
	if (!g_spawn_sync(data->dir, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &status, &error)) {
		printf("Error: %s (%d).\n", error->message, status);
	}
	fflush(stdout);
}

void visualize(SuffixTree *tree, gpointer userData, glong i, glong j) {
	VisualizerData *data = userData;
	data->counter++;
	generateImage(tree, data, i, j);
}
